package main;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.m2m.atl.drivers.emf4atl.ASMEMFModel;
import org.eclipse.m2m.atl.engine.AtlEMFModelHandler;
import org.eclipse.m2m.atl.engine.AtlLauncher;
import org.eclipse.m2m.atl.engine.AtlModelHandler;

public class SplitExecutor {

	private AtlEMFModelHandler modelHandler;
	private ASMEMFModel pnMetamodel;

	private static int SUBNET_ID_LOCATION = 928;
	private static String PETRI_NET_METAMODEL = "resources/placeTransition.ecore";
	private static String NET_WCS_FILE = "resources/net_wcs.ptnet";
	private static String SUBNETS_FILE = "resources/subnets.ptnet";
	private static String DEL_CUT_SET_TRANSFO = "resources/DelCuttingSet.asm";
	private static String CHECK_CONNECTEDNESS_TRANSFO = "resources/CheckConnectedness.asm";
	private static String SPLIT_TRANSFO = "resources/Split.asm";

	private URL delCuttingSetPath;
	private URL checkConnectednessPath;
	private URL splitPath;

	public SplitExecutor() {

	}

	private void createResources() throws MalformedURLException {
		modelHandler = (AtlEMFModelHandler) AtlModelHandler
				.getDefault(AtlModelHandler.AMH_EMF);
		System.out.println("Loading transformation files...");

		File delCuttingSet = new File(DEL_CUT_SET_TRANSFO);
		delCuttingSetPath = delCuttingSet.toURI().toURL();
		File checkConnectedness = new File(CHECK_CONNECTEDNESS_TRANSFO);
		checkConnectednessPath = checkConnectedness.toURI().toURL();
		File split = new File(SPLIT_TRANSFO);
		splitPath = split.toURI().toURL();
	}

	private void initMetamodel(Map<String, Object> models) {
		try {
			System.out.println("Loading PetriNet metamodel...");
			pnMetamodel = (ASMEMFModel) modelHandler.loadModel("PetriNet",
					modelHandler.getMof(), new FileInputStream(
							PETRI_NET_METAMODEL));
			models.put("PetriNet", pnMetamodel);
		} catch (FileNotFoundException e) {
			File temp = new File(PETRI_NET_METAMODEL);
			System.out.println("PetriNet metamodel not found. Folder: "
					+ temp.getAbsolutePath());
		}
	}

	private void deleteCuttingSet(Map<String, Object> models, String netPath,
			String netWCSPath) throws FileNotFoundException {
		System.out.println("Loading models:");
		ASMEMFModel netInputModel = (ASMEMFModel) modelHandler.loadModel("net",
				pnMetamodel, new FileInputStream(netPath));
		System.out.println("- Net model: \"" + netPath + "\" loaded.");
		// ASMEMFModel parInputModel = (ASMEMFModel) modelHandler.loadModel(
		// "parameter", pnMetamodel, new FileInputStream(parameterPath));
		// System.out.println("- Parameter model: \"" + parameterPath
		// + "\" loaded.");
		ASMEMFModel netWCSOutputModel = (ASMEMFModel) modelHandler.newModel(
				"netwcs", netWCSPath, pnMetamodel);

		models.put("net", netInputModel);
		// models.put("parameter", parInputModel);
		models.put("netwcs", netWCSOutputModel);

		System.out.println("Deleting cutting set from the main net...");
		AtlLauncher.getDefault().launch(this.delCuttingSetPath,
				Collections.EMPTY_MAP, models, Collections.EMPTY_MAP,
				Collections.EMPTY_LIST, Collections.EMPTY_MAP);

		modelHandler.saveModel(netWCSOutputModel, netWCSPath, false);
	}

	private void checkConnectedness(Map<String, Object> models, String netPath,
			String netWCSPath, String subnetsPath) throws FileNotFoundException {
		ASMEMFModel netInputModel = (ASMEMFModel) modelHandler.loadModel("net",
				pnMetamodel, new FileInputStream(netPath));
		ASMEMFModel netWCSInputModel = (ASMEMFModel) modelHandler.loadModel(
				"netwcs", pnMetamodel, new FileInputStream(netWCSPath));
		ASMEMFModel subnetsOutputModel = (ASMEMFModel) modelHandler.newModel(
				"subnets", subnetsPath, pnMetamodel);

		models.put("net", netInputModel);
		models.put("netwcs", netWCSInputModel);
		models.put("subnets", subnetsOutputModel);

		System.out
				.println("Checking connectedness of the main net after the cutting set removal...");
		AtlLauncher.getDefault().launch(this.checkConnectednessPath,
				Collections.EMPTY_MAP, models, Collections.EMPTY_MAP,
				Collections.EMPTY_LIST, Collections.EMPTY_MAP);

		modelHandler.saveModel(subnetsOutputModel, subnetsPath, false);
	}

	private void split(Map<String, Object> models, String netPath,
			String netWCSPath, String subnetsPath,
			String splittedPath, int masterSubnet) throws FileNotFoundException {
		ASMEMFModel netInputModel = (ASMEMFModel) modelHandler.loadModel("net",
				pnMetamodel, new FileInputStream(netPath));
		ASMEMFModel netWCSInputModel = (ASMEMFModel) modelHandler.loadModel(
				"netwcs", pnMetamodel, new FileInputStream(netWCSPath));
		ASMEMFModel subnetsInputModel = (ASMEMFModel) modelHandler.loadModel(
				"subnets", pnMetamodel, new FileInputStream(subnetsPath));
		ASMEMFModel splittedOutputModel = (ASMEMFModel) modelHandler.newModel(
				"splitted", splittedPath, pnMetamodel);

		models.put("net", netInputModel);
		models.put("netwcs", netWCSInputModel);
		models.put("subnets", subnetsInputModel);
		models.put("splitted", splittedOutputModel);

		System.out.println("Applying the Split operation rules...");
		System.out.println("- Master subnet: " + masterSubnet);
		AtlLauncher.getDefault().launch(this.splitPath, Collections.EMPTY_MAP,
				models, Collections.EMPTY_MAP, Collections.EMPTY_LIST,
				Collections.EMPTY_MAP);

		modelHandler.saveModel(splittedOutputModel, splittedPath, false);
	}

	private void exec(String netPath, String netWCSPath, String subnetsPath,
			String splittedPath, int masterSubnet) {
		try {
			Map<String, Object> models = new HashMap<String, Object>();
			createResources();
			setSubnet(masterSubnet);
			initMetamodel(models);
			removeTempFile(netWCSPath);
			removeTempFile(subnetsPath);
			removeTempFile(splittedPath);

			deleteCuttingSet(models, netPath, netWCSPath);
			checkConnectedness(models, netPath, netWCSPath, subnetsPath);
			split(models, netPath, netWCSPath, subnetsPath, splittedPath, masterSubnet);

			dispose(models);

			// removeTempFile(netWCSPath);
			// removeTempFile(subnetsPath);
			System.out
					.println("Split operation completed. Splitted net saved as: "
							+ splittedPath);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void setSubnet(int subnet) {
		try {
			File f = new File(SPLIT_TRANSFO);
			RandomAccessFile raf = new RandomAccessFile(f, "rw");

			raf.seek(SUBNET_ID_LOCATION);
			raf.writeBytes(Integer.toString(subnet));
			raf.close();
		} catch (FileNotFoundException e) {
			System.out.println("Transformation file \"" + SPLIT_TRANSFO
					+ "\" not found. Is it in the 'transformations' folder?");
		} catch (IOException e) {
			System.out
					.println("Impossible to access the transformation file \""
							+ SPLIT_TRANSFO + "\"");
		}
	}

	private void removeTempFile(String filepath) {
		File tempFile = new File(filepath);
		tempFile.delete();
	}

	private void dispose(Map<String, Object> models) {
		for (Object model : models.values())
			((ASMEMFModel) model).dispose();
	}

	public static void main(String[] args) {
		/*
		 * if (args.length != 4) { System.out.println("Missing operands.");
		 * System.out
		 * .println("Syntax: net_file splitted_file_destination master_subnet_id"
		 * ); } else {
		 */
		SplitExecutor transformation = new SplitExecutor();
		transformation.exec("resources/exemplo.ptnet", NET_WCS_FILE,
				SUBNETS_FILE, "resources/exemplo_splitted.ptnet", Integer
						.parseInt("4"));
		// transformation.exec(args[0], NET_WCS_FILE,
		// SUBNETS_FILE, args[1], Integer.parseInt(args[3]));
		// }
	}
}
